package com.ibm.rules;

import java.math.BigInteger;

import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import com.ibm.util.EntryPair;



public class RuleEntry<T extends Comparable<T>> implements Comparable<RuleEntry<T>> {
	//private RuleCompareOperator op;
	//private T constants;
	private int tagId;
	private Range<T> range;
	
	public boolean eval(T factValue){
		//return RuleCompareOperator.getOperator(op).<T>eval(factValue, constants);
		/*eval by google range*/
//		return range.contains(factValue);
		
		// this code is bad.
		if (factValue.getClass().getName().equals(EntryPair.class.getName()) ) {
			T_Class_Flag = CLASS_NVAL;
		}
		
		
		/*eval by hard coding via java*/
		if(T_Class_Flag == CLASS_VAL){
			double factval_d = ((Number)factValue).doubleValue(); 
			switch(status){
			case LC_RC:
				return factval_d >= lowerb_v && factval_d <= upperb_v;
			case LC_RN:
				return factval_d >= lowerb_v && factval_d < upperb_v;
			case LN_RC:
				return factval_d > lowerb_v && factval_d <= upperb_v;
			case LN_RN:
				return factval_d > lowerb_v && factval_d < upperb_v;
			}
		}else{
			switch(status){
			case LC_RC:
				return factValue.compareTo(lowerb) >=0 && factValue.compareTo(upperb) <= 0;
			case LC_RN:
				return factValue.compareTo(lowerb) >=0 && factValue.compareTo(upperb) < 0;
			case LN_RC:
				return factValue.compareTo(lowerb) >0 && factValue.compareTo(upperb) <= 0;
			case LN_RN:
				return factValue.compareTo(lowerb) >0 && factValue.compareTo(upperb) < 0;
			}			
		}
		
		return true;
	}
	
	public int getTagId(){
		return tagId;
	}
	
	public  Range<T> getRange(){
		return range;
	}
	
	private T lowerb;
	private T upperb;
	private double lowerb_v ;
	private double upperb_v ;
	
	/*C=0: contain equal; N=1: not contain equal L:left R:right T:Type(V=0,NV=1)*/
	public static final byte LC_RC = (byte)0x00;
	public static final byte LC_RN = (byte)0x01;
	public static final byte LN_RC = (byte)0x02;
	public static final byte LN_RN = (byte)0x03;
	public static final byte CLASS_VAL = (byte)0x00;
	public static final byte CLASS_NVAL = (byte)0x01;
	
	public byte status;
	public byte T_Class_Flag;
	
	public boolean hasLeftBound(){
		return range.hasLowerBound();
	}
	
	public boolean hasRightBound(){
		return range.hasUpperBound();
	}
	
	public T getLeftBound(){
		return lowerb;
	}
	
	public T getRightBound(){
		return upperb;
	}
	public BoundType getLeftBoundType(){
		BoundType lbt = range.lowerBoundType();
		return lbt;
	}
	public BoundType getRightBoundType(){
		BoundType upbt = range.upperBoundType();
		return upbt;
	}
	
	public RuleEntry( Range<T> range, int tagId){//RuleCompareOperator op, T constants){
		//this.op = op;
		//this.constants = constants;
		this.range = range;
		this.tagId = tagId;
		lowerb = range.lowerEndpoint();
		upperb = range.upperEndpoint();
		
		BoundType lbt = range.lowerBoundType();
		BoundType hbt = range.upperBoundType();
		status = 0x00;
		/*status": first bit setting, for right range, high bound 0:contain,close, 1:not contain, open*/
		if(hbt == BoundType.OPEN){
			status |= (byte)(0x01);
		}
		/*status": 2nd bit setting, for left range, high bound 0:contain,close, 1:not contain, open*/
		if(lbt == BoundType.OPEN){
			status |= ((byte)(0x01) << 1);
		}
		/*status": 3nd bit setting for Type T, 0: number class, 1: not number class, e.g., string*/
		T_Class_Flag = CLASS_NVAL;
		if(lowerb instanceof Number )/*for long, double*/
		{
			lowerb_v = ((Number) lowerb).doubleValue();
			upperb_v = ((Number) upperb).doubleValue();
			try {
				T_Class_Flag = CLASS_VAL;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}

	BigInteger ruleid;
	public void setRuleID(BigInteger refruleid){
		this.ruleid = refruleid;
	}
	public BigInteger getRuleID(){
		return ruleid;
	}

	@Override
	public int compareTo(RuleEntry<T> obj) {
		return tagId - obj.getTagId();
	}
}
